﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Crank.Core
{
    #region Extensions

    using Crank.Core.ForignKeyModelTransform;
    using System.Text.RegularExpressions;

    namespace ForignKeyModelTransform
    {
        internal static class ForeignKeyTransformDataModelExtensions
        {
            private static IDictionary<string, int> ExpectedKeyCounts;

            static ForeignKeyTransformDataModelExtensions()
            {
                ExpectedKeyCounts = new Dictionary<string, int>();
            }

            public static void CalculateExpectedKeys(this DataModel model)
            {
                ExpectedKeyCounts = new Dictionary<string, int>();
                foreach (var entity in model.Entities.Values)
                    ExpectedKeyCounts[entity.Name] = entity.KeyCount();
                foreach (var link in model.Links)
                    if (link.Detail.IsKey)
                        ExpectedKeyCounts[link.LHS.Name] += Math.Max(link.RHS.KeyCount(), 1);
                // HACK: This is wrong but it gives a good starting picture
                // A Check at the end is required
            }

            public static bool HasAllKeys(this EntityDef entity)
            {
                return entity.ExpectedKeyCount() <= entity.KeyCount();
            }


            public static int ExpectedKeyCount(this EntityDef entity)
            {
                return ExpectedKeyCounts[entity.Name];
            }

            public static int KeyCount(this EntityDef entity)
            {
                return entity.Attributes.Where(a => a.IsKey).Count(a => true);
            }


        }
    }

    #endregion


    class ForeignKeyModelTransformer : IModelTransformer
    {
        #region IModelTransformer Members

        public void ApplyTransform(DataModel target)
        {
            target.CalculateExpectedKeys();

            var LinksToCreate = target.Links.Where(l => l.SourceColumns.Count == 0);

            bool made_change;

            do
            {
                made_change = false;
                foreach (var link in LinksToCreate)
                {
                    if (link.RHS.HasAllKeys())
                    {
                        made_change = true;
                        AddFKColumns(link);
                    }
                }
                LinksToCreate = target.Links.Where(l => l.SourceColumns.Count == 0);
            } while (made_change);

            if (LinksToCreate.Count(l => true) > 0)
                throw new Exception("Link(s) could not be resolved");

            Regex reg = new Regex(@"^\[(.*)\]$");
            foreach (var link in target.Links)
            {
                link.Detail.Name = reg.Replace(link.Detail.Name, "$1");
            }

            CheckConsistency(target);

        }

        private void CheckConsistency(DataModel model)
        {
            foreach (var link in model.Links)
                if (link.SourceColumns.Count != link.RHS.KeyCount())
                    throw new Exception("Link fails consistency check");
        }

        private void AddFKColumns(LinkDef link)
        {
            foreach (var key in link.RHS.Keys)
            {
                var columnName = "";
                if (link.Detail.Name.Contains("]"))
                {
                    Regex reg = new Regex(@"^\[(.*)\]$");
                    columnName = reg.Replace(link.Detail.Name, "$1");
                }
                else
                {
                    columnName = link.Detail.Name + key.Name;
                }

                AttributeDef fkCol = new AttributeDef(columnName);
                fkCol.Type = key.Type;
                fkCol.IsRequired = link.Detail.IsRequired;
                fkCol.IsKey = link.Detail.IsKey;
                link.LHS.Attributes.Add(fkCol);
                link.SourceColumns.Add(columnName);
            }
        }

        #endregion
    }
}
